﻿#region Copyright (c) 2000-2008 Developer Express Inc.
/*
{*******************************************************************}
{                                                                   }
{       Developer Express .NET Component Library                    }
{       AgDataGrid                                 }
{                                                                   }
{       Copyright (c) 2000-2008 Developer Express Inc.              }
{       ALL RIGHTS RESERVED                                         }
{                                                                   }
{   The entire contents of this file is protected by U.S. and       }
{   International Copyright Laws. Unauthorized reproduction,        }
{   reverse-engineering, and distribution of all or any portion of  }
{   the code contained in this file is strictly prohibited and may  }
{   result in severe civil and criminal penalties and will be       }
{   prosecuted to the maximum extent possible under the law.        }
{                                                                   }
{   RESTRICTIONS                                                    }
{                                                                   }
{   THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES           }
{   ARE CONFIDENTIAL AND PROPRIETARY TRADE                          }
{   SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS   }
{   LICENSED TO DISTRIBUTE THE PRODUCT AND ALL ACCOMPANYING .NET    }
{   CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY.                 }
{                                                                   }
{   THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED      }
{   FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE        }
{   COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE       }
{   AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT  }
{   AND PERMISSION FROM DEVELOPER EXPRESS INC.                      }
{                                                                   }
{   CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON       }
{   ADDITIONAL RESTRICTIONS.                                        }
{                                                                   }
{*******************************************************************}
*/
#endregion Copyright (c) 2000-2008 Developer Express Inc.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using DevExpress.AgMenu.Utils;
namespace DevExpress.AgMenu.Internal {
	public interface IAgMenuPanel {
		Size DesiredSize { get; }
		double IconAreaLeft { get; }
		double IconAreaWidth { get; }
		UIElement GetItemByIndex(int i);
	}
	public interface IAgMenuPanelChild {
		Size DesiredSize { get; }
		HorizontalAlignment HorizontalAlignment { get; }
		double IconAreaLeft { get; }
		double IconAreaWidth { get; set; }
		bool IsFirstVisibleChild { set; }
		bool IsLastVisibleChild { set; }
		VerticalAlignment VerticalAlignment { get; }
		void PrepareForVerticalLayout(List<IAgMenuPanelChild> items);
	}
	public class AgMenuPanel : System.Windows.Controls.Panel, IAgMenuScrollViewerContent, IAgMenuPanel {
		public static readonly DependencyProperty ChildrenOffsetXProperty = DependencyProperty.Register("ChildrenOffsetX", typeof(double),
			typeof(AgMenuPanel), new PropertyMetadata(OnChildrenOffsetXPropertyChanged));
		public static readonly DependencyProperty ChildrenOffsetYProperty = DependencyProperty.Register("ChildrenOffsetY", typeof(double),
			typeof(AgMenuPanel), new PropertyMetadata(OnChildrenOffsetYPropertyChanged));
		public static readonly DependencyProperty IsMultiLineProperty = DependencyProperty.Register("IsMultiLine", typeof(bool),
			typeof(AgMenuPanel), new PropertyMetadata(OnIsMultiLinePropertyChanged));
		public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation),
			typeof(AgMenuPanel), new PropertyMetadata(OnOrientationPropertyChanged));
		public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register("Padding", typeof(Thickness),
			typeof(AgMenuPanel), new PropertyMetadata(OnPaddingPropertyChanged));
		double iconAreaLeft;
		double iconAreaWidth;
		Rect[] calculatedChildBoundsList;
		Size calculatedSize;
		IAgMenuPanelLayoutCalculator layoutCalculator;
		IAgMenuPanelOwner owner;
		public double ChildrenOffsetX {
			get { return (double)GetValue(ChildrenOffsetXProperty); }
			set { SetValue(ChildrenOffsetXProperty, value); }
		}
		public double ChildrenOffsetY {
			get { return (double)GetValue(ChildrenOffsetYProperty); }
			set { SetValue(ChildrenOffsetYProperty, value); }
		}
		public bool IsMultiLine {
			get { return (bool)GetValue(IsMultiLineProperty); }
			set { SetValue(IsMultiLineProperty, value); }
		}
		public Orientation Orientation {
			get { return (Orientation)GetValue(OrientationProperty); }
			set { SetValue(OrientationProperty, value); }
		}
		public Thickness Padding {
			get { return (Thickness)GetValue(PaddingProperty); }
			set { SetValue(PaddingProperty, value); }
		}
		protected IAgMenuPanelLayoutCalculator LayoutCalculator {
			get {
				if(layoutCalculator == null)
					layoutCalculator = CreateLayoutCalculator();
				return layoutCalculator;
			}
		}
		public AgMenuPanel() {
			LayoutUpdated += OnLayoutUpdated;
		}
		protected override Size ArrangeOverride(Size finalSize) {
			LayoutCalculator.CorrectLayout(
				FillLayoutCalculatorInputData(finalSize), ref calculatedChildBoundsList, ref calculatedSize);
			int i = 0;
			UIElement firstVisibleChild = null, lastVisibleChild = null;
			foreach(UIElement child in Children)
				if(child.Visibility == Visibility.Visible) {
					Rect bounds = this.calculatedChildBoundsList[i];
					if(bounds.Width != 0 && bounds.Height != 0) {
						if(firstVisibleChild == null)
							firstVisibleChild = child;
						lastVisibleChild = child;
					}
					child.Arrange(this.calculatedChildBoundsList[i++]);
				}
			foreach(IAgMenuPanelChild child in Children) {
				child.IsFirstVisibleChild = child == firstVisibleChild;
				child.IsLastVisibleChild = child == lastVisibleChild;
			}
			UpdateIconAreaValues();
			return finalSize;
		}
		protected virtual void ChildrenOffsetXChanged(double oldValue) {
			InvalidateMeasure();
		}
		protected virtual void ChildrenOffsetYChanged(double oldValue) {
			InvalidateMeasure();
		}
		protected virtual IAgMenuPanelLayoutCalculator CreateLayoutCalculator() {
			if(Orientation == Orientation.Horizontal)
				return new AgMenuPanelHorizontalLayoutCalculator();
			else
				return new AgMenuPanelVerticalLayoutCalculator();
		}
		protected virtual void IsMultiLineChanged() {
			InvalidateMeasure();
		}
		protected override Size MeasureOverride(Size availableSize) {
			foreach(UIElement child in Children)
				child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
			CalcLayout(availableSize);
			return calculatedSize;
		}
		void OnLayoutUpdated(object sender, EventArgs e) {
			IAgMenuScrollViewerChildrenOwner scrollOwner = GetOwner(typeof(IAgMenuScrollViewerChildrenOwner)) as IAgMenuScrollViewerChildrenOwner;
			if(scrollOwner != null) scrollOwner.SetChild(this);
			IAgMenuPanelOwner _owner = GetOwner(typeof(IAgMenuPanelOwner)) as IAgMenuPanelOwner;
			if(_owner != owner) {
				if(owner != null) {
					if(owner.ItemsHost == this)
						owner.ItemsHost = null;
					owner.OrientationChanged -= OnOwnerOrientationChanged;
				}
				owner = _owner;
				if(owner != null) {
					owner.ItemsHost = this;
					owner.OrientationChanged += OnOwnerOrientationChanged;
				}
				UpdateOrientationFromOwner();
			}
		}
		protected virtual void OrientationChanged(Orientation oldValue) {
			UpdateLayoutCalculator();
			InvalidateMeasure();
		}
		protected virtual void PaddingChanged(Thickness oldValue) {
			InvalidateMeasure();
		}
		protected void UpdateLayoutCalculator() {
			layoutCalculator = null;
		}
		void CalcLayout(Size availableSize) {
			LayoutCalculator.CalcLayout(
				FillLayoutCalculatorInputData(availableSize), out calculatedChildBoundsList, out calculatedSize);
		}
		object GetOwner(Type t) {
			FrameworkElement parent = this.GetParent();
			while(parent != null && !t.IsInstanceOfType(parent))
				parent = parent.GetParent();
			return parent;
		}
		AgMenuPanelLayoutCalculatorInputData FillLayoutCalculatorInputData(Size availableSize) {
			List<IAgMenuPanelChild> children = new List<IAgMenuPanelChild>();
			foreach(UIElement child in Children)
				if(child.Visibility == Visibility.Visible)
					children.Add((IAgMenuPanelChild)child);
			return new AgMenuPanelLayoutCalculatorInputData() {
				AvailableSize = availableSize,
				Children = children,
				ChildrenOffsetX = ChildrenOffsetX,
				ChildrenOffsetY = ChildrenOffsetY,
				IsMultiLine = IsMultiLine,
				Padding = Padding,
				PanelSize = new Size(Width, Height)
			};
		}
		static void OnChildrenOffsetXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			(d as AgMenuPanel).ChildrenOffsetXChanged((double)e.OldValue);
		}
		static void OnChildrenOffsetYPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			(d as AgMenuPanel).ChildrenOffsetYChanged((double)e.OldValue);
		}
		static void OnIsMultiLinePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			(d as AgMenuPanel).IsMultiLineChanged();
		}
		static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			(d as AgMenuPanel).OrientationChanged((Orientation)e.OldValue);
		}
		void OnOwnerOrientationChanged(object sender, EventArgs e) {
			UpdateOrientationFromOwner();
		}
		static void OnPaddingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			(d as AgMenuPanel).PaddingChanged((Thickness)e.OldValue);
		}
		void UpdateIconAreaValues() {
			if(Orientation == Orientation.Vertical && Children.Count > 0) {
				IAgMenuPanelChild ichild = (IAgMenuPanelChild)Children[0];
				this.iconAreaLeft = ichild.IconAreaLeft + ((FrameworkElement)Children[0]).GetPositionNew(this).X;
				this.iconAreaWidth = ichild.IconAreaWidth;
			} else {
				this.iconAreaLeft = 0;
				this.iconAreaWidth = 0;
			}
		}
		void UpdateOrientationFromOwner() {
			if(owner != null)
				Orientation = owner.Orientation;
		}
		#region IAgMenuPanel members
		Size IAgMenuPanel.DesiredSize {
			get { return DesiredSize; }
		}
		double IAgMenuPanel.IconAreaLeft {
			get { return this.iconAreaLeft; }
		}
		double IAgMenuPanel.IconAreaWidth {
			get { return this.iconAreaWidth; }
		}
		#endregion
		#region IAgMenuScrollViewerContent Members
		int IAgMenuScrollViewerContent.ChildCount { get { return Children.Count; } }
		void IAgMenuScrollViewerContent.AdjustHeightForFullyVisibleItems(ref double height) {
			for(int i = 0; i < this.calculatedChildBoundsList.Length; i++)
				if(this.calculatedChildBoundsList[i].Bottom > height) {
					if(i > 0) {
						height = this.calculatedChildBoundsList[i - 1].Bottom;
					}
					break;
				}
		}
		double IAgMenuScrollViewerContent.GetChildHeight(int index) {
			if(index < Children.Count)
				return (Children[index] as FrameworkElement).ActualHeight;
			else
				return 0;
		}
		#endregion
		#region IAgMenuPanel Members
		public UIElement GetItemByIndex(int i) {
			return Children[i];
		}
		#endregion
	}
	public struct AgMenuPanelLayoutCalculatorInputData {
		public Size AvailableSize;
		public List<IAgMenuPanelChild> Children;
		public double ChildrenOffsetX;
		public double ChildrenOffsetY;
		public bool IsMultiLine;
		public Thickness Padding;
		public Size PanelSize;
	}
	public interface IAgMenuPanelLayoutCalculator {
		void CalcLayout(AgMenuPanelLayoutCalculatorInputData inputData, out Rect[] childBoundsList, out Size panelSize);
		void CorrectLayout(AgMenuPanelLayoutCalculatorInputData inputData, ref Rect[] calculatedChildBoundsList, ref Size calculatedSize);
	}
	public class AgMenuPanelLayoutCalculatorBase : IAgMenuPanelLayoutCalculator {
		protected virtual void DoCalcLayout(AgMenuPanelLayoutCalculatorInputData inputData, out Rect[] childBoundsList, out Size panelSize) {
			childBoundsList = new Rect[inputData.Children.Count];
			panelSize = new Size();
		}
		protected virtual void DoCorrectLayout(AgMenuPanelLayoutCalculatorInputData inputData, ref Rect[] calculatedChildBoundsList, ref Size calculatedSize) {
		}
		void IAgMenuPanelLayoutCalculator.CalcLayout(AgMenuPanelLayoutCalculatorInputData inputData, out Rect[] childBoundsList, out Size panelSize) {
			DoCalcLayout(inputData, out childBoundsList, out panelSize);
		}
		void IAgMenuPanelLayoutCalculator.CorrectLayout(AgMenuPanelLayoutCalculatorInputData inputData, ref Rect[] calculatedChildBoundsList, ref Size calculatedSize) {
			DoCorrectLayout(inputData, ref calculatedChildBoundsList, ref calculatedSize);
		}
	}
	public class AgMenuPanelHorizontalLayoutCalculator : AgMenuPanelLayoutCalculatorBase {
		protected override void DoCalcLayout(AgMenuPanelLayoutCalculatorInputData inputData, out Rect[] childBoundsList, out Size panelSize) {
			base.DoCalcLayout(inputData, out childBoundsList, out panelSize);
			double rowHeight = 0, x = inputData.Padding.Left, xMax = inputData.Padding.Left, y = inputData.Padding.Top;
			int rowFirstChildIndex = 0;
			for(int i = 0; i < inputData.Children.Count; i++) {
				Size childSize = inputData.Children[i].DesiredSize;
				if(childSize.Width == 0 || childSize.Height == 0) continue;
				if(inputData.IsMultiLine && i > rowFirstChildIndex &&
						x + inputData.ChildrenOffsetX + childSize.Width > inputData.AvailableSize.Width - inputData.Padding.Right) {
					PositionRowChildrenVertically(inputData.Children, childBoundsList, rowFirstChildIndex, i - 1, rowHeight);
					x = inputData.Padding.Left;
					y += inputData.ChildrenOffsetY + rowHeight;
					rowHeight = 0;
					rowFirstChildIndex = i;
				}
				if(i > rowFirstChildIndex)
					x += inputData.ChildrenOffsetX;
				childBoundsList[i] = new Rect(x, y, childSize.Width, childSize.Height);
				x += childSize.Width;
				xMax = Math.Max(xMax, x);
				rowHeight = Math.Max(rowHeight, childSize.Height);
			}
			if(inputData.Children.Count > 0) {
				if(!inputData.IsMultiLine && !Double.IsNaN(inputData.PanelSize.Height))
					rowHeight = inputData.PanelSize.Height;
				PositionRowChildrenVertically(inputData.Children, childBoundsList, rowFirstChildIndex, inputData.Children.Count - 1, rowHeight);
			}
			panelSize = new Size(xMax + inputData.Padding.Right, y + rowHeight + inputData.Padding.Bottom);
			if(panelSize.Width < inputData.AvailableSize.Width)
				CorrectAlignment(childBoundsList, inputData, ref panelSize);
		}
		private void CorrectAlignment(Rect[] childBoundsList, AgMenuPanelLayoutCalculatorInputData inputData, ref Size panelSize) {
			if(double.IsInfinity(inputData.AvailableSize.Width)) return;
			for(int i = inputData.Children.Count - 1; i >= 0; i--) {
				Size childSize = inputData.Children[i].DesiredSize;
				if(childSize.Width == 0 || childSize.Height == 0) continue;
				if(inputData.Children[i].HorizontalAlignment != HorizontalAlignment.Right) break;
				childBoundsList[i].X += inputData.AvailableSize.Width - panelSize.Width;
			}
			if(inputData.Children.Count > 0 && inputData.Children[inputData.Children.Count-1].HorizontalAlignment == HorizontalAlignment.Right) 
				panelSize.Width = inputData.AvailableSize.Width;
		}
		void PositionRowChildrenVertically(List<IAgMenuPanelChild> children, Rect[] childBoundsList,
				int firstChildIndex, int lastChildIndex, double rowHeight) {
			for(int i = firstChildIndex; i <= lastChildIndex; i++) {
				switch(children[i].VerticalAlignment) {
					case VerticalAlignment.Center:
						RectHelper.Offset(ref childBoundsList[i], 0, (rowHeight - childBoundsList[i].Height) / 2);
						break;
					case VerticalAlignment.Bottom:
						RectHelper.Offset(ref childBoundsList[i], 0, rowHeight - childBoundsList[i].Height);
						break;
					case VerticalAlignment.Stretch:
						childBoundsList[i].Height = rowHeight;
						break;
				}
			}
		}
	}
	public class AgMenuPanelVerticalLayoutCalculator : AgMenuPanelLayoutCalculatorBase {
		protected override void DoCalcLayout(AgMenuPanelLayoutCalculatorInputData inputData, out Rect[] childBoundsList, out Size panelSize) {
			base.DoCalcLayout(inputData, out childBoundsList, out panelSize);
			if(inputData.Children.Count > 0)
				inputData.Children[0].PrepareForVerticalLayout(inputData.Children);
			double colWidth = 0, x = inputData.Padding.Left, y = inputData.Padding.Top, yMax = inputData.Padding.Top;
			int colFirstChildIndex = 0;
			for(int i = 0; i < inputData.Children.Count; i++) {
				Size childSize = inputData.Children[i].DesiredSize;
				if(childSize.Height == 0) continue;
				if(inputData.IsMultiLine && i > colFirstChildIndex &&
						y + inputData.ChildrenOffsetY + childSize.Height > inputData.AvailableSize.Height - inputData.Padding.Bottom) {
					StretchColumnChildren(childBoundsList, colFirstChildIndex, i - 1, colWidth);
					x += inputData.ChildrenOffsetX + colWidth;
					y = inputData.Padding.Top;
					colWidth = 0;
					colFirstChildIndex = i;
				}
				if(i > colFirstChildIndex)
					y += inputData.ChildrenOffsetY;
				childBoundsList[i] = new Rect(x, y, childSize.Width, childSize.Height);
				y += childSize.Height;
				yMax = Math.Max(yMax, y);
				colWidth = Math.Max(colWidth, childSize.Width);
			}
			if(inputData.Children.Count > 0)
				StretchColumnChildren(childBoundsList, colFirstChildIndex, inputData.Children.Count - 1, colWidth);
			panelSize = new Size(x + colWidth + inputData.Padding.Right, yMax + inputData.Padding.Bottom);
		}
		protected override void DoCorrectLayout(AgMenuPanelLayoutCalculatorInputData inputData, ref Rect[] calculatedChildBoundsList, ref Size calculatedSize) {
			if(inputData.IsMultiLine) return;
			double childLeftBound = inputData.Padding.Left;
			double childWidth = inputData.AvailableSize.Width - inputData.Padding.Left - inputData.Padding.Right;
			for(int i = 0; i < calculatedChildBoundsList.Length; i++) {
				Rect bounds = calculatedChildBoundsList[i];
				if(bounds.Width != 0 && bounds.Height != 0) {
					bounds.X = childLeftBound;
					bounds.Width = childWidth;
					calculatedChildBoundsList[i] = bounds;
				}
			}
		}
		void StretchColumnChildren(Rect[] childBoundsList, int firstChildIndex, int lastChildIndex, double colWidth) {
			for(int i = firstChildIndex; i <= lastChildIndex; i++)
				childBoundsList[i].Width = colWidth;
		}
	}
}
